import {
  ConstraintType,
  CurveType,
  EventsRes,
  IArcOfCircleParams,
  ICurve,
  ILineParams,
  SceneGraph,
} from '@amcax/base';
import { wsApi } from '@app-cad/server';
import { Vector2tuple, Vector3 } from 'three-legacy';
import {
  SKETCH_CENTEREVENTS,
  sketchEventCenter,
} from '../../../events/SketchEventCenter';
import CmdBase, { PickType } from '../../CmdBase';
import { getAngle, isEffectiveValue } from '@app-cad/common/utils';
import { dimension } from '../dimension/Dimension';
import { ctx2 } from '@app-cad/module';
import { sketchStore } from '@app-cad/common/utils/sketchTools/useSketchStore';
import { loadEmit } from '@app-base/common/events/CoreEvent';

type SnapType = {
  curveId: string;
  pickType: PickType;
  point?: Vector3;
};

enum State {
  NONE,
  DRAW_CIRCLE,
  DRAW_ARC,
  FINISH,
}

export default class CmdTangencyArcInteractor extends CmdBase {
  startPoint: Vector3; // 绘制起点
  endPoint: Vector3; // 绘制终点
  otherPoint: Vector3; // 线,圆弧的另一个端点
  startPointId: string;
  pickedLine: ICurve;
  vector: Vector2tuple;
  startAngle: number; // 起始角度
  endAngle: number; // 结束角度
  currentState: State = State.NONE;
  private secSnapPoint: SnapType;
  private isClockwise: boolean = false;
  private xCenter: Vector3;
  constructor(
    public onFinishDraw?: (curve: ICurve) => void,
    public allowContinuousCreate = false,
    public needSetEventCenterState = true,
  ) {
    super();
    this.registerEvents();
  }

  reset() {
    this.currentState = State.NONE;
    this.startPoint = null;
    this.endPoint = null;
    this.otherPoint = null;
    this.secSnapPoint = null;
    this.isClockwise = null;
    this.xCenter = null;
    this.startPointId = null;
  }

  registerEvents() {
    if (this.needSetEventCenterState) {
      sketchEventCenter().setState('SketchCircle');
    }
    sketchEventCenter()
      .on(SKETCH_CENTEREVENTS.POINTERMOVE, this.onMouseMove)
      .on(SKETCH_CENTEREVENTS.POINTERDOWN, this.onMouseDown)
      .on(SKETCH_CENTEREVENTS.KEYUP, this.onKeyUp)
      .addExit(this.destroy.bind(this));
  }

  unregisterEvents() {
    if (this.needSetEventCenterState) {
      sketchEventCenter().setDefault();
    }
    this.reset();
    sketchEventCenter()
      .off(SKETCH_CENTEREVENTS.POINTERMOVE, this.onMouseMove)
      .off(SKETCH_CENTEREVENTS.POINTERDOWN, this.onMouseDown)
      .off(SKETCH_CENTEREVENTS.KEYUP, this.onKeyUp);
  }

  releaseObjects() {
    dimension().destroy();
    this.assisGroup.clear();
    this.clearLine();
    SceneGraph.emptyGroup(this.assisGroup);
    ctx2.viewer.requestRender();
  }

  onMouseMove = (e: EventsRes) => {
    this.setMouseWV(e, true);
    if (this.currentState === State.NONE) {
      return;
    }
    if (this.currentState === State.DRAW_ARC) {
      this.endPoint = this.mouseWV.clone();
    }
    this.createAssistPoint(this.mouseWV.clone(), true);
    this.preview();
  };

  getStartPoint = (point: Vector3, line: ICurve) => {
    const params = line?.params as ILineParams | IArcOfCircleParams;
    const startPoint = new Vector3(params?.point1[0], params?.point1[1]);
    const endPoint = new Vector3(params?.point2[0], params?.point2[1]);
    const distanceToStartSq = new Vector3()
      .subVectors(point, startPoint)
      .lengthSq();
    const distanceToEndSq = new Vector3()
      .subVectors(point, endPoint)
      .lengthSq();

    const useStartPoint = distanceToStartSq < distanceToEndSq;

    const resultPoint = (useStartPoint ? startPoint : endPoint).clone();

    const startPointId =
      line.type === 'ArcOfCircle' ? line?.relative[1] : line?.relative[0];
    const endPointId =
      line.type === 'ArcOfCircle' ? line?.relative[2] : line?.relative[1];
    return {
      resultPoint,
      resultPointId: useStartPoint ? startPointId : endPointId,
    };
  };

  getDirectionVector = (point: Vector3, line: ICurve) => {
    const params = line?.params as ILineParams | IArcOfCircleParams;
    const point1 = new Vector3(params?.point1?.[0], params?.point1?.[1], 0);
    const point2 = new Vector3(params?.point2?.[0], params?.point2?.[1], 0);

    const isTargetPointAtPoint1 = point.x === point1.x && point.y === point1.y;
    // 圆弧
    this.otherPoint = isTargetPointAtPoint1 ? point2 : point1;
    if (line.type === 'ArcOfCircle') {
      const center = (params as IArcOfCircleParams)?.center;
      const centerVector = new Vector3(...center);
      const normal = new Vector3()
        .subVectors(isTargetPointAtPoint1 ? point1 : point2, centerVector)
        .normalize();

      const calcIsICurveClockwise = (curve: ICurve) => {
        const points = curve.vertices
          .slice(0, 2)
          .map((v) => new Vector3(v[0] - center[0], v[1] - center[1]));

        return new Vector3().crossVectors(points[0], points[1]).z < 0;
      };
      const isClockwise = calcIsICurveClockwise(line);

      const result = normal.cross(
        new Vector3(
          0,
          0,
          (isClockwise && !isTargetPointAtPoint1) ||
          (isClockwise && !isTargetPointAtPoint1)
            ? 1
            : -1,
        ),
      );

      return result;
    }

    return new Vector3()
      .subVectors(
        isTargetPointAtPoint1 ? point1 : point2,
        isTargetPointAtPoint1 ? point2 : point1,
      )
      .normalize();
  };

  // 通过圆弧切线方向和起点与终点得到圆心的位置
  getCicleCenter = (start: Vector3, end: Vector3, vector: Vector3) => {
    const x1 = start.x;
    const y1 = start.y;
    const x2 = end.x;
    const y2 = end.y;
    const vx = vector.x;
    const vy = vector.y;
    const c1 = x2 ** 2 - x1 ** 2 + y2 ** 2 - y1 ** 2;
    const a1 = -2 * x1 + 2 * x2;
    const b1 = -2 * y1 + 2 * y2;
    const c2 = vx * x1 + vy * y1;
    const a2 = vx;
    const b2 = vy;

    const determinant = a1 * b2 - a2 * b1;

    const x = (c1 * b2 - c2 * b1) / determinant;
    const y = (a1 * c2 - a2 * c1) / determinant;

    return [x, y];
  };

  getLineInfo = (e: EventsRes) => {
    const lineInfo = this.getPickLine(e);
    if (lineInfo == null) {
      return null;
    }
    const type = lineInfo?.type as CurveType;
    const id = lineInfo?.id;
    if (type === 'Point') {
      const curves = sketchStore.getState().curves;
      const result = curves.find(
        (item) =>
          (item.type === 'Line' || item.type === 'ArcOfCircle') &&
          item?.relative.includes(id),
      );
      return result;
    }
    // 只支持圆弧和线
    if (type !== 'ArcOfCircle' && type !== 'Line') {
      return null;
    }
    // 坐标不支持
    if (id === 'X-AXIS' || id === 'Y-AXIS') {
      return null;
    }
    return lineInfo;
  };

  setStartPoint(pointId: string, curve: ICurve) {
    this.startPointId = pointId;
    const pointData = sketchStore
      .getState()
      .curves.find((c) => c.id === pointId);

    this.startPoint = new Vector3(...pointData.vertices[0]);
    this.pickedLine = curve;
    this.currentState = State.DRAW_ARC;
  }

  onMouseDown = (e: EventsRes) => {
    if (e.button.onLeft && this.currentState !== State.FINISH) {
      // 鼠标左键，进行绘制
      if (this.currentState === State.NONE) {
        const lineInfo = this.getLineInfo(e);
        if (lineInfo == null) {
          return;
        }
        const pickedPoint = this.mouseWV.clone();
        const { resultPoint, resultPointId } = this.getStartPoint(
          pickedPoint,
          lineInfo,
        );
        this.startPoint = resultPoint;
        this.startPointId = resultPointId;
        this.pickedLine = lineInfo;
        this.currentState = State.DRAW_ARC;
      } else if (this.currentState === State.DRAW_ARC) {
        const point = this.mouseWV.clone();
        this.endPoint = point;
        if (this.pickType !== PickType.Null) {
          this.secSnapPoint = {
            pickType: this.pickType,
            curveId: this.pickSnapInfo.id,
          };
        }
        this.currentState = State.FINISH;
        this.finishDraw();
      }
      this.createAssistPoint(this.mouseWV.clone());
    } else if (e.button.onRight && !this.closeRightClick) {
      this.currentState = State.NONE;
      // 鼠标右键，取消绘制
      this.destroy();
    }
  };

  getClockwise(start: Vector3, end: Vector3, vector: Vector2tuple) {
    const endVector = {
      x: end.x - start.x,
      y: end.y - start.y,
    };
    // 计算叉积
    const crossProduct = vector[0] * endVector.y - vector[1] * endVector.x;
    if (crossProduct > 0) {
      return false;
    } else {
      return true;
    }
  }

  preview() {
    if (
      this.currentState === State.NONE ||
      this.currentState === State.FINISH
    ) {
      return;
    }
    const circleInfo = this.getCircleInfo();
    const center = circleInfo.center;
    const radius = circleInfo.radius;
    if (isEffectiveValue(center) && isEffectiveValue(radius)) {
      this.createAssistPoint(center, true, 1);
      // // 绘制圆
      let params = {
        x: center.x,
        y: center.y,
        radius: radius,
        startAngle: 0,
        endAngle: Math.PI * 2,
        clockWise: this.isClockwise,
      };
      if (this.currentState === State.DRAW_ARC) {
        this.isClockwise = this.getClockwise(
          this.startPoint,
          this.endPoint,
          this.vector,
        );
        this.startAngle = getAngle(this.startPoint, center);
        this.endAngle = getAngle(this.endPoint, center);
        this.clearLine();
        params = {
          ...params,
          ...{
            startAngle: this.startAngle,
            endAngle: this.endAngle,
            clockWise: this.isClockwise,
          },
        };
        this.createUpdateLine(this.getArcVertexs(params), 'line');
        dimension().dimRadius(
          center,
          this.isClockwise
            ? [this.endPoint, this.startPoint]
            : [this.startPoint, this.endPoint],
          radius,
        ); // 画半径
      }
      ctx2.viewer.requestRender();
    }
  }

  // 获取圆弧的中心点等信息
  getCircleInfo() {
    if (this.currentState === State.DRAW_ARC) {
      const vec = this.getDirectionVector(this.startPoint, this.pickedLine);
      this.vector = [vec.x, vec.y];
      const center = this.getCicleCenter(this.startPoint, this.endPoint, vec);
      const centerV = new Vector3(center[0], center[1], 0);
      this.xCenter = centerV;
      const radius = this.xCenter.distanceTo(this.startPoint);
      return {
        center: centerV,
        radius: radius,
      };
    }
    return null;
  }

  enterKeyDown = () => {
    if (this.currentState === State.DRAW_ARC) {
      // 相切圆弧半径大于0
      const flag = dimension().determineDim(0);
      if (flag) {
        this.setRadius();
        this.preview();
        this.currentState = State.FINISH;
        setTimeout(() => {
          this.finishDraw();
        }, 10);
      }
    }
  };

  setRadius() {
    if (dimension().dimType === 'create') {
      this.endPoint = this.getArcVertex();
    }
  }

  sameVectorsDirection(v1: number[], v2: number[]) {
    const [x1, y1] = v1;
    const [x2, y2] = v2; // 检查是否任何向量为零向量
    if ((x1 === 0 && y1 === 0) || (x2 === 0 && y2 === 0)) {
      return false; // 零向量没有方向
    } // 检查比率是否一致，避免除以零
    if (x2 !== 0 && ((x1 / x2) * y1) / y2 < 0) return false;
    return true;
  }

  // 获取圆弧的顶点
  getArcVertex() {
    const radius = dimension().getDimInfo().value;
    const angle = this.endAngle - this.startAngle;
    const x1 = this.startPoint.x;
    const y1 = this.startPoint.y;
    const center = this.xCenter.clone();
    const normal = new Vector3()
      .subVectors(center, this.startPoint)
      .normalize();

    // 计算圆心坐标
    // 圆心在起始点法向量方向上，距离为半径
    // const normal = [dx / length, dy / length];
    const centerX = x1 + normal.x * radius; // 圆心X坐标
    const centerY = y1 + normal.y * radius; // 圆心Y坐标

    // 计算圆弧结束点的位置
    // 起始点到圆心的向量是 (x1 - centerX, y1 - centerY)
    const deltaX = x1 - centerX;
    const deltaY = y1 - centerY;

    // 旋转公式（绕圆心旋转角度 angle）
    const endX = centerX + deltaX * Math.cos(angle) - deltaY * Math.sin(angle);
    const endY = centerY + deltaX * Math.sin(angle) + deltaY * Math.cos(angle);

    return new Vector3(endX, endY, 0);
  }

  isLoading = false;

  async finishDraw() {
    if (this.isLoading) {
      return;
    }

    try {
      const annotation = dimension().addDimension();
      const p1 = this.startPointId;
      const p2 =
        this.secSnapPoint?.pickType === PickType.Point
          ? this.secSnapPoint?.curveId
          : [this.endPoint.x, this.endPoint.y];
      this.isLoading = true;
      loadEmit(true);

      const res = await wsApi.createSketchArcOfCircle(this.sketchId, {
        mode: 'vector-2point',
        point1: p1,
        point2: p2,
        vector: this.vector,
        constraints: this.getNewConstraint(),
        annotations: [annotation],
        isClockWise: this.isClockwise,
      });
      const result = await this.render({ added: res.added }, !!annotation);

      const newCurve = result.addData?.curves?.find(
        (c) => c.type === 'ArcOfCircle',
      );

      this.onFinishDraw?.(newCurve);

      if (this.allowContinuousCreate) {
        this.setStartPoint(newCurve.relative[2], newCurve);
      }
    } catch (error) {
      console.error(error);
    } finally {
      this.isLoading = false;
      loadEmit(false);
    }
    if (!this.allowContinuousCreate) {
      this.reset();
      this.releaseObjects();
    }
  }
  getNewConstraint() {
    const constraints = [];
    const type = ConstraintType.Tangent;
    constraints.push({
      curve: this.pickedLine.id,
      target: '',
      type,
    });
    return constraints;
  }
}
